/*
 * Copyright (C), 2005-2018, 上海瑛麒动漫科技有限公司
 * FileName: LogUtils.java
 * Author:   tianmu
 * Date:     2018年3月2日 上午9:30:20
 * Description: //模块目的、功能描述
 * History: //修改记录
 * <author>      <time>      <version>    <desc>
 * 修改人姓名             修改时间            版本号                  描述
 */
package com.sfang.common.util.log;

import com.alibaba.fastjson.JSON;
import com.sfang.common.base.constant.StringCode;
import com.sfang.common.util.http.HttpServletUtil;
import com.sfang.common.util.string.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.StreamUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.management.ManagementFactory;
import java.net.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

/**
 * 〈一句话功能简述〉<br>
 * 〈功能详细描述〉
 *
 * @author tianmu
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
@Component
public class LogUtils {

    @Autowired
    private Environment environment;

    @Value("${spring.application.name:}")
    private String applicationName;
    private static String appKey;

    @PostConstruct
    public void init() {
        this.appKey = applicationName;
    }


    private static Logger logger = LoggerFactory.getLogger(LogUtils.class);

    /**
     * 换行符
     */
    private static final String LINE_BREAKS = "[\\r\\n]";

    /**
     * 功能描述: <br>
     * 打印请求
     *
     * @param request
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void logRequest(HttpServletRequest request) {
        logRequest(StringCode.EMPTY, request, null);
    }

    /**
     * 功能描述: <br>
     * 打印请求
     *
     * @param request
     * @param cause
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void logRequest(HttpServletRequest request, Throwable cause) {
        logRequest(StringCode.EMPTY, request, cause);
    }

    /**
     * 功能描述: <br>
     * 打印请求
     *
     * @param message
     * @param request
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void logRequest(String message, HttpServletRequest request) {
        logRequest(message, request, null);
    }

    /**
     * 功能描述: <br>
     * 打印请求
     *
     * @param message
     * @param request
     * @param cause
     * @see [相关类/方法](可选)
     * @since [产品/模块版本](可选)
     */
    public static void logRequest(String message, HttpServletRequest request, Throwable cause) {
        if (request.getQueryString() == null && cause == null) {
            logger.error(message + " Request is: {}",
                    request.getRequestURI().replaceAll(LINE_BREAKS, StringCode.EMPTY));
        } else if (request.getQueryString() != null && cause == null) {
            try {
                logger.error(message + " Request is: {}?{}",
                        request.getRequestURI().replaceAll(LINE_BREAKS, StringCode.EMPTY),
                        URLDecoder.decode(request.getQueryString(), StandardCharsets.UTF_8.name())
                                .replaceAll(LINE_BREAKS, StringCode.EMPTY));
            } catch (UnsupportedEncodingException e) {
                // do nothing
            }
        } else if (request.getQueryString() == null && cause != null) {
            logger.error(message + " Request is: {}", request.getRequestURI().replaceAll(LINE_BREAKS, StringCode.EMPTY),
                    cause);
        } else if (request.getQueryString() != null && cause != null) {
            try {
                logger.error(message + " Request is: {}?{}",
                        request.getRequestURI().replaceAll(LINE_BREAKS, StringCode.EMPTY),
                        URLDecoder.decode(request.getQueryString(), StandardCharsets.UTF_8.name())
                                .replaceAll(LINE_BREAKS, StringCode.EMPTY),
                        cause);
            } catch (UnsupportedEncodingException e) {
                // do nothing
            }
        }
    }

    public static String assemblyLogContent(String message) {
        return assemblyLogContent(null, null, null, message, null, null, null, null, null, false);
    }

    public static String assemblyLogContent(String message, HttpServletRequest request) {
        return assemblyLogContent(null, null, null, message, null, null, null, request, null, false);
    }

    public static String assemblyAppLogContent(String message, HttpServletRequest request) {
        return assemblyLogContent(null, null, null, message, null, null, null, request, null, true);
    }

    public static String assemblyAppLogContent(Integer userId,
                                               String message, HttpServletRequest request) {
        return assemblyLogContent(userId, null, null, message, null, null, null, request, null, true);
    }

    public static String assemblyAppLogContent(Integer userId, String orderNo,
                                               String message, HttpServletRequest request) {
        return assemblyLogContent(userId, orderNo, null, message, null, null, null, request, null, true);
    }

    public static String assemblyAppLogContent(Integer userId, String orderNo,
                                               String message, String payload, HttpServletRequest request) {
        return assemblyLogContent(userId, orderNo, null, message, payload, null, null, request, null, true);
    }

    /*timestamp
    uuid
    appKey
    severity
    serverIp
    serverHostName
    pid
    userId
    orderNo
    requestIp
    requestUrl
    message
    version
    logger
    requestReferer
    requestAuth
    requestHeader
    requestBody
    response
    exception
    eventType
    payload*/
    public static String assemblyLogContent(Integer userId, String orderNo, Integer eventType,
                                            String message, String payload, String version, String response, HttpServletRequest request, Exception e,
                                            boolean resolveRequestBody) {
        try {
            appKey = Optional.ofNullable(appKey).orElse(" ");
            message = Optional.ofNullable(message).orElse(" ");
            payload = Optional.ofNullable(payload).orElse(" ");
            version = Optional.ofNullable(version).orElse(" ");
            response = Optional.ofNullable(response).orElse(" ");
            orderNo = Optional.ofNullable(orderNo).orElse(" ");

            String requestReferer = " ";
            String requestAuth = " ";
            String requestHeader = " ";
            String requestBody = " ";
            String requestIp = " ";
            String requestUrl = " ";

            if (request != null)
                try {
                    requestReferer = Optional.ofNullable(request.getHeader("Referer")).orElse("");
                } catch (IllegalStateException err) {
                    request = null;
                }

            if (request != null) {
                requestAuth = Optional.ofNullable(request.getAuthType()).orElse(" ");
                requestIp = HttpServletUtil.getIpV6Addr(request);
                requestUrl = request.getRequestURL().toString();

                Map headersMap = HttpServletUtil.assemblyHeaders(request);
                requestHeader = headersMap != null ? headersMap.toString() : " ";

                if (resolveRequestBody)
                    try {
                        requestBody = StreamUtils.copyToString(request.getInputStream(), Charset.forName("UTF-8"));
                    } catch (IOException err) {
                        err.printStackTrace();
                    }

                requestBody = Optional.ofNullable(requestBody).orElse(" ");

            }

            String serverIp = "";
            String serverHostName = "";
            InetAddress localInetAddress = getLocalInetAddress();

            if (localInetAddress != null) {
                serverIp = localInetAddress.getHostAddress();
                serverHostName = localInetAddress.getHostAddress();
            }

            String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
            String uuid = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();

            String logContent = String.format("%s~%s~%s~%s~%s~"
                            + "%s~%s~%s~%s~%s~"
                            + "%s~%s~%s~%s~%s~"
                            + "%s",
                    uuid,
                    pid, userId != null ? userId.toString() : "",
                    orderNo, requestIp, requestUrl, message, version, requestReferer, requestAuth, requestHeader,
                    StringUtils.isNotEmpty(requestBody) ? requestBody : " ",
                    StringUtils.isNotEmpty(response) ? response : " ",
                    e != null ? JSON.toJSONString(e) : " ",
                    eventType != null ? String.valueOf(eventType) : " ",
                    StringUtils.isNotEmpty(payload) ? payload : " "
            );

            return logContent;

        } catch (Exception err) {
            return String.format("LogUtils error:%s", JSON.toJSONString(err));
        }
    }

    public static InetAddress getLocalInetAddress() {
        try {
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface ni = networkInterfaces.nextElement();
                Enumeration<InetAddress> nias = ni.getInetAddresses();
                while (nias.hasMoreElements()) {
                    InetAddress ia = nias.nextElement();
                    if (!ia.isLinkLocalAddress() && !ia.isLoopbackAddress() && ia instanceof Inet4Address) {
                        return ia;
                    }
                }
            }
        } catch (SocketException e) {
            logger.error(String.valueOf(e.getStackTrace()));
        }
        return null;
    }

    public static String getRequestBody(HttpServletRequest request) {
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder("");
        try {
            br = request.getReader();
            String str;
            while ((str = br.readLine()) != null) {
                sb.append(str);
            }
            br.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != br) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }
}
